package io.github.iogogogo.commons.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * Created by tao.zeng on 2024/11/7.
 */
@Slf4j
public class ReflectUtils {

    /**
     * 通过字段名从对象或对象的父类中得到字段的值
     *
     * @param object    对象实例
     * @param fieldName 字段名称
     * @return 字段对应的值
     */
    public static Object getValue(Object object, String fieldName) {

        if (Objects.isNull(object) || StringUtils.isBlank(fieldName)) return null;

        try {
            Class<?> clazz = object.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(object);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 通过字段名从对象或对象的父类中得到字段的值
     *
     * @param object    对象实例
     * @param fieldName 字段名称
     * @return 字段对应的值
     */
    public static Object getValue0(Object object, String fieldName) {

        if (Objects.isNull(object) || StringUtils.isBlank(fieldName)) return null;

        Class<?> clazz = object.getClass();

        Field field;
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(object);
            } catch (Exception e) {
                // 这里的异常必须这样写，不能抛出去。如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
                // LOGGER.error(e.getMessage(), e);
            }
        }
        return null;
    }

    /**
     * 通过字段名从对象或对象的父类中得到字段的值（调用字段的get方法）
     *
     * @param object    对象实例
     * @param fieldName 字段名称
     * @return 字段对应的值
     */
    public static Object getValueOfGet(Object object, String fieldName) {

        if (Objects.isNull(object) || StringUtils.isBlank(fieldName)) return null;

        Class<?> clazz = object.getClass();

        Field field;
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);

                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                // 获得get方法
                Method getMethod = pd.getReadMethod();
                // 执行get方法返回一个Object
                return getMethod.invoke(object);
            } catch (Exception e) {
                // 这里的异常必须这样写，不能抛出去。如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
            }
        }
        return null;
    }

    /**
     * 通过字段名从对象或对象的父类中得到字段的值（调用字典的get方法，可以取出复杂的对象的值）
     *
     * @param object    对象实例
     * @param fieldName 字段名称
     * @return 字段对应的值
     */
    public static Object getValueOfGetIncludeObjectField(Object object, String fieldName) {

        if (Objects.isNull(object) || StringUtils.isBlank(fieldName)) return null;

        if (HashMap.class.equals(object.getClass())) return ((Map<?, ?>) object).get(fieldName);

        Class<?> clazz = object.getClass();

        Field field;
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {

                if (StringUtils.contains(fieldName, ".")) {
                    // 如：operatorUser.name、operatorUser.org.name，递归调用
                    String[] splitFiledName = StringUtils.split(fieldName, "\\.");
                    return getValueOfGetIncludeObjectField(getValueOfGetIncludeObjectField(object, splitFiledName[0]), splitFiledName[1]);
                }

                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);

                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                // 获得get方法
                Method getMethod = pd.getReadMethod();
                // 执行get方法返回一个Object
                return getMethod.invoke(object);
            } catch (Exception e) {
                // 这里的异常必须这样写，不能抛出去。如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
            }
        }
        return null;
    }
}
